package com.uniview.configuration;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.script.DefaultRedisScript;

/**
 * created by lux
 * 分布式锁
 */
@Slf4j
public class RedisLockConfiguration {
    @Autowired
    Environment env;

    /**
     * @desc redis分布式锁的应用
     * 单机模式
     */
    @Bean
    @Qualifier("redissionClient")
    public RedissonClient redissonClient(){
        RedissonClient redissonClient;
        Config config = new Config();
        String url = "redis://" + env.getProperty("spring.redis.host") + ":" + env.getProperty("spring.redis.port");
        config.useSingleServer().setAddress(url)
                .setDatabase(Integer.valueOf(env.getProperty("spring.redis.database")));
        config.setLockWatchdogTimeout(30*60*1000);
        //哨兵模式示例
         /*config.useSentinelServers().addSentinelAddress(
                "redis://172.29.3.245:26378","redis://172.29.3.245:26379", "redis://172.29.3.245:26380")
                .setMasterName("mymaster")
                .setPassword("a123456").setDatabase(0);*/
        //集群模式示例
         /*config.useClusterServers().addNodeAddress(
                "redis://172.29.3.245:6375","redis://172.29.3.245:6376", "redis://172.29.3.245:6377",
                "redis://172.29.3.245:6378","redis://172.29.3.245:6379", "redis://172.29.3.245:6380")
                .setPassword("a123456").setScanInterval(5000);*/
        try {
            redissonClient = Redisson.create(config);
            return redissonClient;
        } catch (Exception e) {
            log.error("RedissonClient init redis url:[{}], Exception:", url, e);
            throw e;
        }
    }
    /**
     * @desc redis分布式锁的应用
     * 终极红锁模式避免看门狗模式中突然机器宕机，锁还没来得及转移造成两个客户端同时拥有锁的问题
     * 但是此种方式开销巨大（需要给一半以上的节点上锁），并且要求每一个节点都是单独的不存在主从复制的问题，条件过于苛刻
     * ---由此引进高可用性和高一致性的取舍问题，zookeeper保证了高一致性
     */
    @Bean
    @Qualifier("redissonRedLock")
    public RedissonRedLock redissonRedLock(){
        Config config1 = new Config();
        config1.useSingleServer().setAddress("redis://192.168.0.1:5378")
                .setPassword("a123456").setDatabase(0);
        RedissonClient redissonClient1 = Redisson.create(config1);

        Config config2 = new Config();
        config2.useSingleServer().setAddress("redis://192.168.0.1:5379")
                .setPassword("a123456").setDatabase(0);
        RedissonClient redissonClient2 = Redisson.create(config2);

        Config config3 = new Config();
        config3.useSingleServer().setAddress("redis://192.168.0.1:5380")
                .setPassword("a123456").setDatabase(0);
        RedissonClient redissonClient3 = Redisson.create(config3);

        String resourceName = "REDLOCK_KEY";

        RLock lock1 = redissonClient1.getLock(resourceName);
        RLock lock2 = redissonClient2.getLock(resourceName);
        RLock lock3 = redissonClient3.getLock(resourceName);
        // 向3个redis实例尝试加锁
        RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
//        boolean isLock;
//        try {
//            // isLock = redLock.tryLock();
//            // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。
//            isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
//            System.out.println("isLock = "+isLock);
//            if (isLock) {
//                //TODO if get lock success, do something;
//            }
//        } catch (Exception e) {
//        } finally {
//            // 无论如何, 最后都要解锁
//            redLock.unlock();
//        }
        return redLock;
    }
    /**
     * @desc 执行lua脚本配置
     * 删除lua脚本
     */
    @Bean
    @Qualifier("deleteRedisScript")
    public DefaultRedisScript<String> deleteRedisScript() {
        DefaultRedisScript<String> deleteRedisScript = new DefaultRedisScript<>();
        deleteRedisScript.setResultType(String.class);
        deleteRedisScript.setScriptText(env.getProperty("delete.lua"));
        return deleteRedisScript;
    }

    /**
     * @desc 初始化lua脚本
     */
    @Bean
    @Qualifier("initRedisScript")
    public DefaultRedisScript<String> initRedisScript() {
        DefaultRedisScript<String> initRedisScript = new DefaultRedisScript<>();
        initRedisScript.setResultType(String.class);
        initRedisScript.setScriptText(env.getProperty("delete.lua"));
        return initRedisScript;
    }
}
